﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;

namespace OK.SCOM.Authoring.Tools.MPElementsExtract
{
	enum NodeType { Nothing, ScriptName, ScriptBody, Script };
	enum ProcessingMode {Extract, Embed};

	struct ProcessingResult
	{
		internal NodeType type;
		internal String value;
		internal XmlNode valueNode;
		internal String name;
		internal XmlNode nameNode;

		internal ProcessingResult(NodeType t, string v, string n)
		{
			type = t;
			value = v;
			name = n;
			valueNode = null;
			nameNode = null;
		}
		internal ProcessingResult(NodeType t)
		{
			type = t;
			value = "";
			name = "";
			valueNode = null;
			nameNode = null;
		}
	}

	class MPScriptProcessor
	{
		private XmlDocument _doc;
		private DirectoryInfo _targetDir;
		Dictionary<String, ProcessingResult> _scripts;
		Dictionary<String, String> _subScripts;

		public MPScriptProcessor(XmlDocument doc)
		{
			_doc = doc;
			_scripts = new Dictionary<String, ProcessingResult>();
			_subScripts = new Dictionary<string, string>();
		}

		public void ExtractScripts(DirectoryInfo targetDir, bool ReplaceWithPlacemarks)
		{
			_targetDir = targetDir;
			foreach (XmlNode node in _doc.ChildNodes)
			{
				ProcessNode(node);
			}
			SaveScripts(ReplaceWithPlacemarks);
			_targetDir = null;
		}

		public void EmbedScripts(DirectoryInfo targetDir)
		{
			foreach (XmlNode node in _doc.ChildNodes)
			{
				ProcessNode(node);
			}
			foreach (string script in _scripts.Keys)
			{
				string newScriptBody = "";

				string[] lines = _scripts[script].value.Split(new String[] { "\r\n" }, StringSplitOptions.None);
				foreach (string line in lines)
				{
					if (line.StartsWith("{{") && line.EndsWith("}}"))
					{
						string file = targetDir.FullName+"\\SubScripts\\"+line.Replace("{{", "").Replace("}}", "");
						if (File.Exists(file))
						{
							StreamReader reader = File.OpenText(file);
							string content = reader.ReadToEnd();
							reader.Close();
							if (newScriptBody.Length > 0) newScriptBody += "\r\n";
							newScriptBody += content;
						}
						else throw new FileNotFoundException("Cannot find script file", file);
					}
				}

				_scripts[script].valueNode.InnerText = newScriptBody;
			}
		}

		private ProcessingResult ProcessNode(XmlNode node)
		{
			//Console.WriteLine(node.Name);
			ProcessingResult res = new ProcessingResult(NodeType.Nothing);
			if (node.Name == "ScriptName")
			{
				res.type = NodeType.ScriptName;
				res.name = node.InnerText;
				res.nameNode = node;
			}
			else if (node.Name == "ScriptBody")
			{
				res.type = NodeType.ScriptBody;
				res.value = node.InnerText;
				res.valueNode = node;
			}

			foreach (XmlNode cnode in node.ChildNodes)
			{
				ProcessingResult cres = ProcessNode(cnode);
				if (cres.type == NodeType.ScriptName)
				{
					res.type = NodeType.Script;
					res.name = cres.name;
					res.nameNode = cres.nameNode;
				}
				else if (cres.type == NodeType.ScriptBody)
				{
					res.type = NodeType.Script;
					res.value = cres.value;
					res.valueNode = cres.valueNode;
				}
				else if (cres.type == NodeType.Script)
				{
					if (cres.name.Length == 0 || _scripts.ContainsKey(cres.name))
					{
						cres.name = Guid.NewGuid().ToString();
					}
					_scripts.Add(cres.name, cres);
					WriteInfo("Script found: " + cres.name);
				}
			}

			return res;
		}

		private void SaveScripts(bool ReplaceWithPlacemarks)
		{
			foreach (string script in _scripts.Keys)
			{
				try
				{
					if((script.StartsWith("$") && script.EndsWith("$")) || script.Contains("/"))
					{
						throw new Exception(String.Format("Invalid script name: {0}",script));
					}
					else
					{
						string scriptFile=_targetDir.FullName + "\\" + script;
						WriteInfo("Writing script: " + scriptFile);
						WriteContent(scriptFile, _scripts[script].value);
						string PlaceMarks = SaveSubScripts(script, _scripts[script].value);
						if (ReplaceWithPlacemarks)
						{
							_scripts[script].valueNode.InnerText = PlaceMarks;
						}
						WriteInfo("Script length: " + _scripts[script].value.Length.ToString());
					}
				}
				catch(Exception ex)
				{
					WriteInfo(String.Format("Exception: {0}",ex.Message));
				}
			}
		}

		private string SaveSubScripts(String scriptName, String scriptBody)
		{
			//			string scriptDir = "SubScripts_" + scriptName;
			string scriptDir = "SubScripts";
			string scriptFullDir = _targetDir.FullName + "\\" + scriptDir;
			if (!Directory.Exists(scriptFullDir)) _targetDir.CreateSubdirectory(scriptDir);

			string[] lines = scriptBody.Split(new String[] { "\r\n" }, StringSplitOptions.None);
			string subScriptBody = "";
			string scriptBodyNew = "";
			string subScriptName = scriptName;
			string subScriptFullName = scriptFullDir+"\\"+subScriptName;
			if (File.Exists(subScriptFullName)) File.Delete(subScriptFullName);
			foreach (string line in lines)
			{
				if (line.Contains("#####"))
				{
					if (subScriptName.Length > 0 && subScriptBody.Length > 0)
					{
						string scriptNameNew = ManageSubScriptFile(scriptFullDir, subScriptName, subScriptBody, scriptName);
						if (scriptBodyNew.Length > 0) scriptBodyNew += "\r\n";
						scriptBodyNew += "{{" + scriptNameNew + "}}";
					}

					int idx = line.IndexOf("#####");
					//subScriptName = line.Substring(idx + 5).Replace(" ", "").Replace("..", "").Replace("\\", "_");
					subScriptName = line.Substring(idx + 5).Replace(" ", "").Replace("..", "").Replace(":", "");
					subScriptFullName = scriptFullDir + "\\" + subScriptName;
					subScriptBody = "";
				}
				if (subScriptName != "")
				{
					if (subScriptBody.Length > 0) subScriptBody += "\r\n";
					subScriptBody += line;
				}
			}
			if (subScriptName.Length > 0 && subScriptBody.Length > 0)
			{
				string scriptNameNew = ManageSubScriptFile(scriptFullDir, subScriptName, subScriptBody, scriptName);
				if (scriptBodyNew.Length > 0) scriptBodyNew += "\r\n";
				scriptBodyNew += "{{" + scriptNameNew + "}}";
			}

			return scriptBodyNew;

		}

		private string ManageSubScriptFile(string FullDir, string Name, string Content, string suffix)
		{
			string ScriptName = Name;
			string ScriptContent = Content;
			string subScriptFullName = FullDir + "\\" + ScriptName;

			if (_subScripts.ContainsKey(ScriptName) && _subScripts[ScriptName] != ScriptContent)
			{
				if (File.Exists(subScriptFullName))
				{
					string ext = ScriptName.Substring(ScriptName.LastIndexOf("."));
					ScriptName = ScriptName.Substring(0, ScriptName.Length - ext.Length) + "__" + suffix.Replace(".", "_") + ext;
					subScriptFullName = FullDir + "\\" + ScriptName;
					if (File.Exists(subScriptFullName)) File.Delete(subScriptFullName);
				}
				WriteContent(subScriptFullName, Content);
				_subScripts.Add(ScriptName,Content);
			}
			else if (!_subScripts.ContainsKey(ScriptName))
			{
				if (File.Exists(subScriptFullName)) File.Delete(subScriptFullName);
				WriteContent(subScriptFullName, Content);
				_subScripts.Add(ScriptName, Content);
			}

			return ScriptName;
		}

		private void WriteContent(String fileName, String Content)
		{
			TextWriter writer = new StreamWriter(fileName, false);
			writer.Write(Content);
			writer.Close();
		}

		private void WriteInfo(string message)
		{
			Console.WriteLine(message);
		}
	}


}
